#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import re
import threading
import bs4 as bs4
import requests
from types import NoneType
from datetime import datetime
from bs4 import BeautifulSoup
from util import StringUtil


class Ticket:
    """
    票据
    """
    name = None
    _type = None
    image_url = None
    status = None
    address = None
    open_time = None
    price = None

    def __init__(self, name: str, _type: str, image_url: str, status: str, address: str, open_time: str, price: str):
        """
        创建一个票据
        :param name: 名称
        :param _type: 类型
        :param image_url: 封面
        :param status: 状态
        :param address: 演出地址
        :param open_time: 开售时间
        :param price: 价格(去整)
        """
        self.name = name
        self._type = _type
        self.image_url = image_url
        self.status = status
        self.address = address
        self.open_time = open_time
        self.price = price


class Runner:
    __obj_list = []
    __inner_log_tpl = "{} [{}] {}"
    __default_headers = {
        "sec-ch-ua": 'Chromium";v="104", " Not A;Brand";v="99", "Google Chrome";v="104',
        "sec-ch-ua-platform": "macOS",
        "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36"
    }

    def __init__(self, last_page, page_num):
        self.last_page = last_page
        self.page_num = 0

    def run(self, url: str, text: str) -> None | str:
        try:
            _id, _city_name = self.get_drama_city_sort(self.parse_page_text_to_list(text))
            # self.fetch_page_by_id(url, _id, headers=None)
            # 获取城市
            _page_id = self.get_bind_bs4("https://www.moretickets.com/{}", _id, _city_name)
            _performance_url, _performance_name = self.get_performance_sort(_page_id)
            _page_detail = self.get_bind_bs4("https://www.moretickets.com{}", _performance_url, _performance_name)
            # 获取时间
            _page_time_url, _time_name = self.get_time_sort(_page_detail, _performance_url)
            _all_detail = self.get_bind_bs4("https://www.moretickets.com{}", _page_time_url, _time_name)
            self.get_last_page(_all_detail)
            return self.get_choose_page(_page_time_url)

        except TypeError as err:
            self.__print_error("{}".format(err))

    # 调用函数开始爬取方法
    def get_to_run(self):
        pass

    def get_bind_bs4(self, url: str, _id: str, _choose_name: str) -> None | str:
        try:
            content = self.fetch_page_by_id(url.format(_id), _choose_name, headers=None)
            return self.parse_page_text_to_list(content)
        except:
            self.__print_error("")

    def fetch_page_by_id(self, url: str, _id: str = 0, headers=None) -> None | str:
        if headers is None:
            headers = {}
        self.__print_info("准备获取页面 {} 的内容...".format(_id))

        url_trimmed = StringUtil.clean_str(url)
        if url_trimmed is None:
            self.__print_error("指定的目标地址为空: {}".format(url_trimmed))
            return None
        if len(headers) <= 0:
            result = requests.get(url=url_trimmed, headers=self.__default_headers)
        else:
            current_headers = self.__default_headers.copy()
            current_headers.update(headers)
            result = requests.get(url=url_trimmed, headers=current_headers)
        if result is None:
            print(self.__print_warn("未响应结果"))
            return None
        return result.text

    def parse_page_text_to_list(self, text: str) -> BeautifulSoup:
        text_trimmed = StringUtil.clean_str(text)
        if text_trimmed is None:
            return None
        curr_res_obj = BeautifulSoup(text_trimmed, "lxml")
        return curr_res_obj

    def get_last_page(self, curr_res_obj: bs4.element.ResultSet):

        curr = curr_res_obj.find_all(href=re.compile("/list/(\d*)-(\w*)/hottest/p\d"))
        self.last_page = curr[-2]['href'].split('/')[-1][-1]

    # 爬取所有分页页面
    def get_choose_page(self, page_url: str):
        threads = []
        self.__print_info("默认爬取当前选项的所有分页")
        # try:
        while int(self.page_num) < int(self.last_page):
            self.page_num += 1
            # domain = "https://www.moretickets.com{}"
            url = page_url + '/p{}'.format(str(self.page_num))
            # chosse_name = "爬取p{}".format(str(self.page_num))
            # try:
            threads.append(threading.Thread(target=self.get_all_detail, name="爬虫{}号".format(self.page_num),
                                            args=(self.get_bind_bs4("https://www.moretickets.com{}", url,
                                           "爬取p{}".format(str(self.page_num))),)))

            # except KeyboardInterrupt:
            #     print("正在终止...")
        print(threads)
        for thread in threads:
            print("正在启动: {}...".format(thread.name))
            thread.start()
            thread.join()
            # detail_obj = self.get_bind_bs4("https://www.moretickets.com{}", url,
            #                                "爬取p{}".format(str(self.page_num)))
            # obj_list_tmp2 = self.get_all_detail(detail_obj)
            # self.get_all_detail(detail_obj)
            # self.__obj_list += obj_list_tmp2
        return self.__obj_list

        # except:
        #     self.__print_error("该分页无内容可爬取！！！")

    # 爬取所有城市id
    def get_drama_city_sort(self, curr_res_obj: bs4.element.ResultSet) -> str | None:
        try:
            city_obj = curr_res_obj.find_all(class_="letter-city single-city")
            city_dict = {}
            for child in city_obj:
                city_dict["{}".format(child["data-cityname"])] = child["data-cityid"]
            self.__print_info("请输入选择购票的城市...\n")
            city_name = StringUtil.choose_performance("选择城市分类")
            self.city = StringUtil.clean_city(city_dict, city_name)
            path_url, tmp_city_name = StringUtil.clean_city(city_dict, city_name)
            return path_url, tmp_city_name
        except TypeError as err:
            self.__print_error("不存在该城市！！！")
            self.__print_error("{}".format(err))

    # 获取演出分类
    def get_performance_sort(self, curr_res_obj: bs4.element.ResultSet) -> str | None:
        try:
            performance_obj = curr_res_obj.find_all(href=re.compile("/list/(\d*)-(\w*)/hottest"))[:9]
            performance_dict = {}
            for performance in performance_obj:
                performance_dict["{}".format(performance.get_text())] = performance["href"]
            self.__print_info("请输入选择购票的演出类型...\n")
            print(list(performance_dict.keys()))
            performance_name = StringUtil.choose_performance("选择演出分类")
            path_url, tmp_performance_name = StringUtil.clean_performance(performance_dict, performance_name)
            return path_url, tmp_performance_name

        except NoneType:
            pass

    # 获取演出详细信息
    def get_all_detail(self, curr_res_obj: bs4.element.ResultSet):
        """
        解析指定的页面信息
        :param text: 页面内容(HTML格式)
        :return: 数据列表
        """
        curr_show_container = curr_res_obj.find(name="div", attrs={"class": "shows-container"})
        all_show_items = curr_show_container.find_all("a", attrs={"class": "show-items sa_entrance",
                                                                  "data-sasectionname": "演出列表"})

        res = []

        for curr_show_items in all_show_items:
            curr_res_show_item_name = curr_show_items.get("data-sashowname")
            if curr_res_show_item_name is None:
                continue

            curr_res_show_item_type = curr_show_items.get("data-showtypedisplayname")
            if curr_res_show_item_type is None:
                continue

            curr_img = curr_show_items.find(name="img")
            if curr_img is None:
                continue

            curr_res_image_url = curr_img.get("data-src")
            if curr_res_image_url is None:
                continue

            curr_tags = curr_show_items.find(name="div", attrs={"class": "tags"})
            if curr_tags is None:
                continue

            curr_tag = curr_tags.find(name="span", attrs={"class": "tag"})
            if curr_tag is None:
                continue

            curr_res_status = curr_tag.text

            curr_res_address = curr_show_items.find(name="div", attrs={"class": "show-addr"})
            if curr_res_address is None:
                continue

            curr_res_address = curr_res_address.text

            curr_res_price = curr_show_items.find(name="div", attrs={"class": "show-price"})
            if curr_res_price is None:
                continue

            curr_res_price = curr_res_price.text

            curr_res_open_time = curr_show_items.find(name="div", attrs={"class": "show-time"})
            if curr_res_open_time is None:
                continue

            curr_res_open_time = curr_res_open_time.text

            open_time_list = str(curr_res_open_time).split("-")

            curr_open_time = None

            if len(open_time_list) > 0:
                first_open_time = open_time_list[0]
                curr_open_time = first_open_time.replace(".", "/")
            else:
                curr_open_time = None

            try:
                number_price = StringUtil.clean_price(str(curr_res_price))
            except ValueError:
                number_price = 0

            res.append(Ticket(
                name=str(curr_res_show_item_name) if len(str(curr_res_show_item_name)) else None,
                _type=str(curr_res_show_item_type) if len(str(curr_res_show_item_type)) else None,
                image_url=str(curr_res_image_url) if len(str(curr_res_image_url)) else None,
                status=str(curr_res_status) if len(str(curr_res_status)) else None,
                address=str(curr_res_address) if len(str(curr_res_address)) else None,
                open_time=str(curr_open_time) if len(str(curr_open_time)) else None,
                price=number_price
            ))
        self.__obj_list += res

    # 选择购票时间
    def get_time_sort(self, curr_res_obj: bs4.element.ResultSet, city_name: str) -> str | None:

        time_obj = curr_res_obj.find_all(href=re.compile("/list/(\d*)-(\w*)/(\w*)-hottest"))
        time_dict = {}
        for time in time_obj:
            time_dict["{}".format(time.get_text())] = time["href"]
        self.__print_info("请输入选择购票的时间类型...\n")
        self.__print_info("[全部 一周内 一月内 本周末]\n")
        time_name = StringUtil.choose_performance("选择时间分类")
        _time_url, _time_name = StringUtil.clean_time(time_dict, time_name, city_name)
        return _time_url, _time_name

    def __get_current_time(self):
        # 获取时间
        return str(datetime.now()).split(".")[0]

    def __print_debug(self, text: str):
        print(self.__inner_log_tpl.format("DEBUG", self.__get_current_time(), StringUtil.clean_str(text)))

    def __print_info(self, text: str):
        print(self.__inner_log_tpl.format("INFO", self.__get_current_time(), StringUtil.clean_str(text)))

    def __print_warn(self, text: str):
        print(self.__inner_log_tpl.format("WARN", self.__get_current_time(), StringUtil.clean_str(text)))

    def __print_error(self, text: str):
        print(self.__inner_log_tpl.format("ERROR", self.__get_current_time(), StringUtil.clean_str(text)))

    def __print_fatal(self, text: str):
        self.__print_error(text)


if __name__ == '__main__':
    pass
